<?php
namespace app\common\model;

/**
 * Created by PhpStorm.
 * User: dev
 * Date: 15/12/3
 * Time: 14:55
 */
class Curl
{

    protected $url, $opts, $curl_opts, $keep, $upload, $data, $code;

    public $error;

    /**
     * 析构函数
     *
     * @param
     *            $opts
     */
    public function __construct($opts = array())
    {
        $this->error = '';
        // curl默认参数
        $this->curl_opts = array(
            CURLOPT_TIMEOUT => 60,
            CURLOPT_HEADER => true,
            CURLOPT_NOBODY => FALSE,
            CURLOPT_USERAGENT => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36",
            CURLOPT_RETURNTRANSFER => TRUE,
            CURLOPT_FOLLOWLOCATION => FALSE,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false
        );
        
        if (count($opts) > 0)
            $this->set_opts($opts);
        
        $this->keep = true;
        $this->upload = false;
        $this->data = array();
    }

    private function set_opts($opts = array())
    {
        foreach ($opts as $key => $val) {
            $this->curl_opts[$key] = $val;
        }
    }
    
    // 魔术方法
    public function __set($key, $val)
    {
        if (is_null($val) || $val == '')
            return false;
        
        switch ($key) {
            case 'timeout':
                if (is_numeric($val))
                    $this->curl_opts[CURLOPT_TIMEOUT] = $val;
                break;
            case 'ref':
                if ($val != '')
                    $this->curl_opts[CURLOPT_REFERER] = $val;
                break;
            case 'head':
                if (gettype($val) == 'array')
                    $this->head($val);
                break;
            case 'url':
                $this->set_url($val);
                break;
            case 'keep':
                $this->set_keep($val);
                break;
            case 'upload':
                $this->upload = $val ? true : false;
                break;
            case 'agent':
                if (gettype($val) == 'array' && count($val) > 1) {
                    $this->agent($val[0], $val[1]);
                }
                break;
            case 'poststr':
                $this->data = $val;
                break;
            case 'post':
                if (gettype($val) == 'array' && count($val) > 1)
                    $this->data = $val;
                break;
            case 'delcookie':
                $this->delcookie($val);
                break;
        }
        return true;
    }

    /**
     * 清除COOKIE
     *
     * @param bool|false $oper            
     */
    private function delcookie($oper = false)
    {
        if ($oper) {
            unset($_SESSION[$this->domain()]);
        }
    }

    /**
     * 得到302跳转地址
     *
     * @param
     *            $data
     * @return string
     */
    public function direct($data)
    {
        if (preg_match_all("/Location: (.*)/i", $data, $match)) {
            $match[1][0] = strtolower(trim($match[1][0]));
            if (preg_match_all("/^[http|https].*/i", $match[1][0], $types)) {
                return $match[1][0];
            } else {
                return $this->domain() . substr($this->link, 0, strrpos($this->link, '/')) . $match[1][0];
            }
        }
        return '';
    }
    
    // 设置代理
    private function agent($ip = '', $port = '')
    {
        $this->curl_opts[CURLOPT_PROXY] = $ip;
        $this->curl_opts[CURLOPT_PROXYPORT] = $port;
    }
    
    // 设置URL
    private function set_url($url)
    {
        $this->curl_opts[CURLOPT_URL] = $url;
    }
    
    // 设置curl参数
    private function head($opts = array())
    {
        foreach ($opts as $opt) {
            $this->curl_opts[CURLOPT_HTTPHEADER][] = $opt;
        }
    }
    
    // 设置是否是持久链接
    private function set_keep($val)
    {
        $this->keep = $val ? true : false;
    }

    /**
     * 开始执行
     *
     * @param bool|true $die            
     * @return array|bool|string
     */
    public function exec($die = true,$deep=0,$maxDeep=5)
    {
        $data = array(
            'code' => $this->code,
            'body' => '',
            'head' => '',
            'location' => ''
        );
        
        if($deep>$maxDeep)
        {
            $this->error="超过请求资源最大尝试连接数";
            $data['code']='01';
            return $data;
        }
        
        $returndata = str_replace("HTTP/1.1 100 Continue\r\n\r\n", "", $this->curl());
        
        $result = explode("\r\n\r\n", $returndata, 2);
        
        // 头部处理
        if (isset($result[0])) {
            $this->header($result[0]);
            $data['head'] = $result[0];
        }
        
        switch ($this->code) {
            case 301:
                $data['location'] = $this->direct($returndata);
                break;
            case 302:
                $data['location'] = $this->direct($returndata);
                break;
            default:
                if (isset($result[1]))
                    $data['body'] = $result[1];
                break;
        }
        
        $data['code'] = $this->code;
        
        if($data['code']==0)
        {
            $deep++;
            return $this->exec($die,$deep,$maxDeep);
        }
        
        
        return $data;
    }
    
    // 头部分析
    private function header($data = '')
    {
        $this->cookie($data);
    }

    /**
     * 返回主域名
     *
     * @return string
     */
    private function domain()
    {
        $str = $this->curl_opts[CURLOPT_URL];
        if ($str != '') {
            if (preg_match_all("/^(http:|https:)\/\/(.*?)\//i", $str, $matchs)) {
                if (count($matchs) > 2) {
                    return $matchs[1][0] . '//' . $matchs[2][0];
                }
            }
        }
        return 0;
    }

    /**
     * 设置或得到cookie
     *
     * @param string $data            
     * @return array|string
     */
    private function cookie($data = '')
    {
        if (! $this->keep)
            return '';
        
        if (preg_match_all("/Set-Cookie\:(.*);/i", $data, $mathchs)) {
            foreach ($mathchs[1] as $key => $mathch) {
                $cookie = explode("=", $mathch, 2);
                if (count($cookie) > 1)
                    $_SESSION[$this->domain()]['cookie'][$cookie[0]] = $cookie[1];
            }
        } elseif (isset($_SESSION[$this->domain()]['cookie']) && gettype($_SESSION[$this->domain()]['cookie']) == 'array') {
            $cookie = '';
            foreach ($_SESSION[$this->domain()]['cookie'] as $key => $val) {
                if (gettype($val) != 'string')
                    continue;
                if ($cookie != '')
                    $cookie .= ";";
                $cookie .= "$key=$val";
            }
            
            return $cookie;
        }
        return '';
    }

    /**
     * 抓取数据
     */
    private function curl()
    {
        $cookie = $this->cookie();
        if ($cookie != '') {
            $this->curl_opts[CURLOPT_COOKIE] = $cookie;
        }
        
        if (in_array(gettype($this->data), array(
            'array',
            'string'
        ))) {
            $this->curl_opts[CURLOPT_POST] = TRUE;
            $this->curl_opts[CURLOPT_POSTFIELDS] = $this->data;
            if ($this->upload)
                $this->curl_opts[CURLOPT_HTTPHEADER][] = "content-type: multipart/form-data";
        }

        $ch = curl_init();
        curl_setopt_array($ch, $this->curl_opts);
        $this->error = curl_error($ch);
        
        try{
            $result = curl_exec($ch);
        }catch (\Exception  $e)
        {
            $result=false;
            $this->code=408;
        }
        
        if ($result === false) {
            $this->error = 'Curl error: ' . curl_error($ch);
        }
        $this->code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if ($this->code == 200 && CURLINFO_CONTENT_LENGTH_DOWNLOAD == 0)
            $this->code = 0;
        curl_close($ch);
        return $result;
    }
}